mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 20:51:12 -06:00 
			
		
		
		
	Implement modifier volumes and port _merge_loops() to XS
This commit is contained in:
		
							parent
							
								
									b17d06f9d1
								
							
						
					
					
						commit
						c8a48b4527
					
				
					 10 changed files with 195 additions and 109 deletions
				
			
		|  | @ -58,69 +58,6 @@ sub flow { | |||
|     ); | ||||
| } | ||||
| 
 | ||||
| # build polylines from lines | ||||
| sub make_surfaces { | ||||
|     my $self = shift; | ||||
|     my ($loops) = @_; | ||||
|      | ||||
|     return if !@$loops; | ||||
|     $self->slices->clear; | ||||
|     $self->slices->append($self->_merge_loops($loops)); | ||||
|      | ||||
|     if (0) { | ||||
|         require "Slic3r/SVG.pm"; | ||||
|         Slic3r::SVG::output("surfaces.svg", | ||||
|             #polylines         => $loops, | ||||
|             red_polylines       => [ grep $_->is_counter_clockwise, @$loops ], | ||||
|             green_polylines     => [ grep !$_->is_counter_clockwise, @$loops ], | ||||
|             expolygons          => [ map $_->expolygon, @{$self->slices} ], | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub _merge_loops { | ||||
|     my ($self, $loops, $safety_offset) = @_; | ||||
|      | ||||
|     # Input loops are not suitable for evenodd nor nonzero fill types, as we might get | ||||
|     # two consecutive concentric loops having the same winding order - and we have to  | ||||
|     # respect such order. In that case, evenodd would create wrong inversions, and nonzero | ||||
|     # would ignore holes inside two concentric contours. | ||||
|     # So we're ordering loops and collapse consecutive concentric loops having the same  | ||||
|     # winding order. | ||||
|     # TODO: find a faster algorithm for this, maybe with some sort of binary search. | ||||
|     # If we computed a "nesting tree" we could also just remove the consecutive loops | ||||
|     # having the same winding order, and remove the extra one(s) so that we could just | ||||
|     # supply everything to offset_ex() instead of performing several union/diff calls. | ||||
|      | ||||
|     # we sort by area assuming that the outermost loops have larger area; | ||||
|     # the previous sorting method, based on $b->contains_point($a->[0]), failed to nest | ||||
|     # loops correctly in some edge cases when original model had overlapping facets | ||||
|     my @abs_area = map abs($_), my @area = map $_->area, @$loops; | ||||
|     my @sorted = sort { $abs_area[$b] <=> $abs_area[$a] } 0..$#$loops;  # outer first | ||||
|      | ||||
|     # we don't perform a safety offset now because it might reverse cw loops | ||||
|     my $slices = []; | ||||
|     for my $i (@sorted) { | ||||
|         # we rely on the already computed area to determine the winding order | ||||
|         # of the loops, since the Orientation() function provided by Clipper | ||||
|         # would do the same, thus repeating the calculation | ||||
|         $slices = ($area[$i] >= 0) | ||||
|             ? [ $loops->[$i], @$slices ] | ||||
|             : diff($slices, [$loops->[$i]]); | ||||
|     } | ||||
|      | ||||
|     # perform a safety offset to merge very close facets (TODO: find test case for this) | ||||
|     $safety_offset //= scale 0.0499; | ||||
|     $slices = offset2_ex($slices, +$safety_offset, -$safety_offset); | ||||
|      | ||||
|     Slic3r::debugf "Layer %d (slice_z = %.2f, print_z = %.2f): %d surface(s) having %d holes detected from %d polylines\n", | ||||
|         $self->id, $self->slice_z, $self->print_z, | ||||
|         scalar(@$slices), scalar(map @{$_->holes}, @$slices), scalar(@$loops) | ||||
|         if $Slic3r::debug; | ||||
|      | ||||
|     return map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), @$slices; | ||||
| } | ||||
| 
 | ||||
| sub make_perimeters { | ||||
|     my $self = shift; | ||||
|      | ||||
|  | @ -318,8 +255,8 @@ sub _fill_gaps { | |||
|     $filler->angle($self->config->fill_angle); | ||||
|     $filler->layer_id($self->layer->id); | ||||
|      | ||||
|     # we should probably use this code to handle thin walls and remove that logic from | ||||
|     # make_surfaces(), but we need to enable dynamic extrusion width before as we can't | ||||
|     # we should probably use this code to handle thin walls | ||||
|     # but we need to enable dynamic extrusion width before as we can't | ||||
|     # use zigzag for thin walls. | ||||
|      | ||||
|     # medial axis-based gap fill should benefit from detection of larger gaps too, so  | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ sub add_object { | |||
|             $new_object->add_volume( | ||||
|                 material_id         => $volume->material_id, | ||||
|                 mesh                => $volume->mesh->clone, | ||||
|                 modifier            => $volume->modifier, | ||||
|             ); | ||||
|              | ||||
|             if (defined $volume->material_id) { | ||||
|  | @ -361,7 +362,7 @@ sub raw_mesh { | |||
|     my $self = shift; | ||||
|      | ||||
|     my $mesh = Slic3r::TriangleMesh->new; | ||||
|     $mesh->merge($_->mesh) for @{ $self->volumes }; | ||||
|     $mesh->merge($_->mesh) for grep !$_->modifier, @{ $self->volumes }; | ||||
|     return $mesh; | ||||
| } | ||||
| 
 | ||||
|  | @ -458,12 +459,12 @@ sub unique_materials { | |||
| 
 | ||||
| sub facets_count { | ||||
|     my $self = shift; | ||||
|     return sum(map $_->mesh->facets_count, @{$self->volumes}); | ||||
|     return sum(map $_->mesh->facets_count, grep !$_->modifier, @{$self->volumes}); | ||||
| } | ||||
| 
 | ||||
| sub needed_repair { | ||||
|     my $self = shift; | ||||
|     return (first { !$_->mesh->needed_repair } @{$self->volumes}) ? 0 : 1; | ||||
|     return (first { !$_->mesh->needed_repair } grep !$_->modifier, @{$self->volumes}) ? 0 : 1; | ||||
| } | ||||
| 
 | ||||
| sub mesh_stats { | ||||
|  | @ -494,7 +495,7 @@ sub print_info { | |||
|             printf "  needed repair:     no\n"; | ||||
|         } | ||||
|     } else { | ||||
|         printf "  number of facets:  %d\n", scalar(map @{$_->facets}, @{$self->volumes}); | ||||
|         printf "  number of facets:  %d\n", scalar(map @{$_->facets}, grep !$_->modifier, @{$self->volumes}); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -504,6 +505,7 @@ use Moo; | |||
| has 'object'            => (is => 'ro', weak_ref => 1, required => 1); | ||||
| has 'material_id'       => (is => 'rw'); | ||||
| has 'mesh'              => (is => 'rw', required => 1); | ||||
| has 'modifier'          => (is => 'rw', defualt => sub { 0 }); | ||||
| 
 | ||||
| package Slic3r::Model::Instance; | ||||
| use Moo; | ||||
|  |  | |||
|  | @ -528,8 +528,7 @@ sub export_svg { | |||
|     my $self = shift; | ||||
|     my %params = @_; | ||||
|      | ||||
|     # this shouldn't be needed, but we're currently relying on ->make_surfaces() which | ||||
|     # calls ->perimeter_flow | ||||
|     # is this needed? | ||||
|     $self->init_extruders; | ||||
|      | ||||
|     $_->slice for @{$self->objects}; | ||||
|  |  | |||
|  | @ -178,35 +178,57 @@ sub slice { | |||
|         $layer->region($_) for 0 .. ($regions_count-1); | ||||
|     } | ||||
|      | ||||
|     # process facets | ||||
|     # get array of Z coordinates for slicing | ||||
|     my @z = map $_->slice_z, @{$self->layers}; | ||||
|      | ||||
|     # slice all non-modifier volumes | ||||
|     for my $region_id (0..$#{$self->region_volumes}) { | ||||
|         next if !defined $self->region_volumes->[$region_id]; | ||||
|          | ||||
|         # compose mesh | ||||
|         my $mesh; | ||||
|         foreach my $volume_id (@{$self->region_volumes->[$region_id]}) { | ||||
|             if (defined $mesh) { | ||||
|                 $mesh->merge($self->model_object->volumes->[$volume_id]->mesh); | ||||
|             } else { | ||||
|                 $mesh = $self->model_object->volumes->[$volume_id]->mesh->clone; | ||||
|         my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0); | ||||
|         for my $layer_id (0..$#$expolygons_by_layer) { | ||||
|             my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; | ||||
|             $layerm->slices->clear; | ||||
|             foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) { | ||||
|                 $layerm->slices->append(Slic3r::Surface->new( | ||||
|                     expolygon    => $expolygon, | ||||
|                     surface_type => S_TYPE_INTERNAL, | ||||
|                 )); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         # transform mesh | ||||
|         # we ignore the per-instance transformations currently and only  | ||||
|         # consider the first one | ||||
|         $self->model_object->instances->[0]->transform_mesh($mesh, 1); | ||||
|          | ||||
|         # align mesh to Z = 0 and apply XY shift | ||||
|         $mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min); | ||||
|          | ||||
|         { | ||||
|             my $loops = $mesh->slice([ map $_->slice_z, @{$self->layers} ]); | ||||
|             for my $layer_id (0..$#$loops) { | ||||
|                 my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; | ||||
|                 $layerm->make_surfaces($loops->[$layer_id]); | ||||
|     } | ||||
|      | ||||
|     # then slice all modifier volumes | ||||
|     if (@{$self->region_volumes} > 1) { | ||||
|         for my $region_id (0..$#{$self->region_volumes}) { | ||||
|             my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1); | ||||
|              | ||||
|             # loop through the other regions and 'steal' the slices belonging to this one | ||||
|             for my $other_region_id (0..$#{$self->region_volumes}) { | ||||
|                 next if $other_region_id == $region_id; | ||||
|                  | ||||
|                 for my $layer_id (0..$#$expolygons_by_layer) { | ||||
|                     my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; | ||||
|                     my $other_layerm = $self->layers->[$layer_id]->regions->[$other_region_id]; | ||||
|                      | ||||
|                     my $other_slices = [ map $_->p, @{$other_layerm->slices} ];  # Polygons | ||||
|                     my $my_parts = intersection_ex( | ||||
|                         $other_slices, | ||||
|                         [ map @$_, @{ $expolygons_by_layer->[$layer_id] } ], | ||||
|                     ); | ||||
|                     next if !@$my_parts; | ||||
|                      | ||||
|                     # append new parts to our region | ||||
|                     foreach my $expolygon (@$my_parts) { | ||||
|                         $layerm->slices->append(Slic3r::Surface->new( | ||||
|                             expolygon    => $expolygon, | ||||
|                             surface_type => S_TYPE_INTERNAL, | ||||
|                         )); | ||||
|                     } | ||||
|                      | ||||
|                     # remove such parts from original region | ||||
|                     $other_layerm->slices->clear; | ||||
|                     $other_layerm->append($_) for @{ diff($other_slices, $my_parts) }; | ||||
|                 } | ||||
|             } | ||||
|             # TODO: read slicing_errors | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | @ -285,6 +307,38 @@ sub slice { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| sub _slice_region { | ||||
|     my ($self, $region_id, $z, $modifier) = @_; | ||||
| 
 | ||||
|     return [] if !defined $self->region_volumes->[$region_id]; | ||||
| 
 | ||||
|     # compose mesh | ||||
|     my $mesh; | ||||
|     foreach my $volume_id (@{$self->region_volumes->[$region_id]}) { | ||||
|         my $volume = $self->model_object->volumes->[$volume_id]; | ||||
|         next if $volume->modifier && !$modifier; | ||||
|         next if !$volume->modifier && $modifier; | ||||
|          | ||||
|         if (defined $mesh) { | ||||
|             $mesh->merge($volume->mesh); | ||||
|         } else { | ||||
|             $mesh = $volume->mesh->clone; | ||||
|         } | ||||
|     } | ||||
|     next if !defined $mesh; | ||||
| 
 | ||||
|     # transform mesh | ||||
|     # we ignore the per-instance transformations currently and only  | ||||
|     # consider the first one | ||||
|     $self->model_object->instances->[0]->transform_mesh($mesh, 1); | ||||
| 
 | ||||
|     # align mesh to Z = 0 and apply XY shift | ||||
|     $mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min); | ||||
|      | ||||
|     # perform actual slicing | ||||
|     return $mesh->slice($z); | ||||
| } | ||||
| 
 | ||||
| sub make_perimeters { | ||||
|     my $self = shift; | ||||
|      | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci