mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 12:11:15 -06:00 
			
		
		
		
	Vojtech likes to use Sublime on Windows to get the wheels rolling.
This commit is contained in:
		
							parent
							
								
									d392858ee3
								
							
						
					
					
						commit
						7da68c91a5
					
				
					 26 changed files with 408 additions and 864 deletions
				
			
		|  | @ -1,230 +0,0 @@ | |||
| package Slic3r::Fill::3DHoneycomb; | ||||
| use Moo; | ||||
| 
 | ||||
| extends 'Slic3r::Fill::Base'; | ||||
| 
 | ||||
| use POSIX qw(ceil fmod); | ||||
| use Slic3r::Geometry qw(scale scaled_epsilon); | ||||
| use Slic3r::Geometry::Clipper qw(intersection_pl); | ||||
| 
 | ||||
| # require bridge flow since most of this pattern hangs in air | ||||
| sub use_bridge_flow { 1 } | ||||
| 
 | ||||
| sub fill_surface { | ||||
|     my ($self, $surface, %params) = @_; | ||||
|      | ||||
|     my $expolygon = $surface->expolygon; | ||||
|     my $bb = $expolygon->bounding_box; | ||||
|     my $size = $bb->size; | ||||
|      | ||||
|     my $distance = scale($self->spacing) / $params{density}; | ||||
|      | ||||
|     # align bounding box to a multiple of our honeycomb grid module | ||||
|     # (a module is 2*$distance since one $distance half-module is  | ||||
|     # growing while the other $distance half-module is shrinking) | ||||
|     { | ||||
|         my $min = $bb->min_point; | ||||
|         $min->translate( | ||||
|             -($bb->x_min % (2*$distance)), | ||||
|             -($bb->y_min % (2*$distance)), | ||||
|         ); | ||||
|         $bb->merge_point($min); | ||||
|     } | ||||
|      | ||||
|     # generate pattern | ||||
|     my @polylines = map Slic3r::Polyline->new(@$_), | ||||
|         makeGrid( | ||||
|             scale($self->z), | ||||
|             $distance, | ||||
|             ceil($size->x / $distance) + 1, | ||||
|             ceil($size->y / $distance) + 1,  #// | ||||
|             (($self->layer_id / $surface->thickness_layers) % 2) + 1, | ||||
|         ); | ||||
|      | ||||
|     # move pattern in place | ||||
|     $_->translate($bb->x_min, $bb->y_min) for @polylines; | ||||
|      | ||||
|     # clip pattern to boundaries | ||||
|     @polylines = @{intersection_pl(\@polylines, \@$expolygon)}; | ||||
|      | ||||
|     # connect lines | ||||
|     unless ($params{dont_connect} || !@polylines) {  # prevent calling leftmost_point() on empty collections | ||||
|         my ($expolygon_off) = @{$expolygon->offset_ex(scaled_epsilon)}; | ||||
|         my $collection = Slic3r::Polyline::Collection->new(@polylines); | ||||
|         @polylines = (); | ||||
|         foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { | ||||
|             # try to append this polyline to previous one if any | ||||
|             if (@polylines) { | ||||
|                 my $line = Slic3r::Line->new($polylines[-1]->last_point, $polyline->first_point); | ||||
|                 if ($line->length <= 1.5*$distance && $expolygon_off->contains_line($line)) { | ||||
|                     $polylines[-1]->append_polyline($polyline); | ||||
|                     next; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             # make a clone before $collection goes out of scope | ||||
|             push @polylines, $polyline->clone; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # TODO: return ExtrusionLoop objects to get better chained paths | ||||
|     return @polylines; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| =head1 DESCRIPTION | ||||
| 
 | ||||
| Creates a contiguous sequence of points at a specified height that make | ||||
| up a horizontal slice of the edges of a space filling truncated | ||||
| octahedron tesselation. The octahedrons are oriented so that the | ||||
| square faces are in the horizontal plane with edges parallel to the X | ||||
| and Y axes. | ||||
| 
 | ||||
| Credits: David Eccles (gringer). | ||||
| 
 | ||||
| =head2 makeGrid(z, gridSize, gridWidth, gridHeight, curveType) | ||||
| 
 | ||||
| Generate a set of curves (array of array of 2d points) that describe a | ||||
| horizontal slice of a truncated regular octahedron with a specified | ||||
| grid square size. | ||||
| 
 | ||||
| =cut | ||||
| 
 | ||||
| sub makeGrid { | ||||
|     my ($z, $gridSize, $gridWidth, $gridHeight, $curveType) = @_; | ||||
|     my $scaleFactor = $gridSize; | ||||
|     my $normalisedZ = $z / $scaleFactor; | ||||
|     my @points = makeNormalisedGrid($normalisedZ, $gridWidth, $gridHeight, $curveType); | ||||
|     foreach my $lineRef (@points) { | ||||
|         foreach my $pointRef (@$lineRef) { | ||||
|             $pointRef->[0] *= $scaleFactor; | ||||
|             $pointRef->[1] *= $scaleFactor; | ||||
|         } | ||||
|     } | ||||
|     return @points; | ||||
| } | ||||
| 
 | ||||
| =head1 FUNCTIONS | ||||
| =cut | ||||
| 
 | ||||
| =head2 colinearPoints(offset, gridLength) | ||||
| 
 | ||||
| Generate an array of points that are in the same direction as the | ||||
| basic printing line (i.e. Y points for columns, X points for rows) | ||||
| 
 | ||||
| Note: a negative offset only causes a change in the perpendicular | ||||
| direction | ||||
| 
 | ||||
| =cut | ||||
| 
 | ||||
| sub colinearPoints { | ||||
|     my ($offset, $baseLocation, $gridLength) = @_; | ||||
|      | ||||
|     my @points = (); | ||||
|     push @points, $baseLocation - abs($offset/2); | ||||
|     for (my $i = 0; $i < $gridLength; $i++) { | ||||
|         push @points, $baseLocation + $i + abs($offset/2); | ||||
|         push @points, $baseLocation + ($i+1) - abs($offset/2); | ||||
|     } | ||||
|     push @points, $baseLocation + $gridLength + abs($offset/2); | ||||
|     return @points; | ||||
| } | ||||
| 
 | ||||
| =head2 colinearPoints(offset, baseLocation, gridLength) | ||||
| 
 | ||||
| Generate an array of points for the dimension that is perpendicular to | ||||
| the basic printing line (i.e. X points for columns, Y points for rows) | ||||
| 
 | ||||
| =cut | ||||
| 
 | ||||
| sub perpendPoints { | ||||
|     my ($offset, $baseLocation, $gridLength) = @_; | ||||
|      | ||||
|     my @points = (); | ||||
|     my $side = 2*(($baseLocation) % 2) - 1; | ||||
|     push @points, $baseLocation - $offset/2 * $side; | ||||
|     for (my $i = 0; $i < $gridLength; $i++) { | ||||
|         $side = 2*(($i+$baseLocation) % 2) - 1; | ||||
|         push @points, $baseLocation + $offset/2 * $side; | ||||
|         push @points, $baseLocation + $offset/2 * $side; | ||||
|     } | ||||
|     push @points, $baseLocation - $offset/2 * $side; | ||||
|      | ||||
|     return @points; | ||||
| } | ||||
| 
 | ||||
| =head2 trim(pointArrayRef, minX, minY, maxX, maxY) | ||||
| 
 | ||||
| Trims an array of points to specified rectangular limits. Point | ||||
| components that are outside these limits are set to the limits. | ||||
| 
 | ||||
| =cut | ||||
| 
 | ||||
| sub trim { | ||||
|     my ($pointArrayRef, $minX, $minY, $maxX, $maxY) = @_; | ||||
|    | ||||
|     foreach (@$pointArrayRef) { | ||||
|         $_->[0] = ($_->[0] < $minX) ? $minX : (($_->[0] > $maxX) ? $maxX : $_->[0]); | ||||
|         $_->[1] = ($_->[1] < $minY) ? $minY : (($_->[1] > $maxY) ? $maxY : $_->[1]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| =head2 makeNormalisedGrid(z, gridWidth, gridHeight, curveType) | ||||
| 
 | ||||
| Generate a set of curves (array of array of 2d points) that describe a | ||||
| horizontal slice of a truncated regular octahedron with edge length 1. | ||||
| 
 | ||||
| curveType specifies which lines to print, 1 for vertical lines | ||||
| (columns), 2 for horizontal lines (rows), and 3 for both. | ||||
| 
 | ||||
| =cut | ||||
| 
 | ||||
| sub makeNormalisedGrid { | ||||
|     my ($z, $gridWidth, $gridHeight, $curveType) = @_; | ||||
|      | ||||
|     ## offset required to create a regular octagram | ||||
|     my $octagramGap = 0.5; | ||||
|      | ||||
|     # sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap] | ||||
|     my $a = sqrt(2);  # period | ||||
|     my $wave = abs(fmod($z, $a) - $a/2)/$a*4 - 1; | ||||
|     my $offset = $wave * $octagramGap; | ||||
|      | ||||
|     my @points = (); | ||||
|     if (($curveType & 1) != 0) { | ||||
|         for (my $x = 0; $x <= $gridWidth; $x++) { | ||||
|             my @xPoints = perpendPoints($offset, $x, $gridHeight); | ||||
|             my @yPoints = colinearPoints($offset, 0, $gridHeight); | ||||
|             # This is essentially @newPoints = zip(@xPoints, @yPoints) | ||||
|             my @newPoints = map [ $xPoints[$_], $yPoints[$_] ], 0..$#xPoints; | ||||
|              | ||||
|             # trim points to grid edges | ||||
|             #trim(\@newPoints, 0, 0, $gridWidth, $gridHeight); | ||||
|              | ||||
|             if ($x % 2 == 0){ | ||||
|                 push @points, [ @newPoints ]; | ||||
|             } else { | ||||
|                 push @points, [ reverse @newPoints ]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if (($curveType & 2) != 0) { | ||||
|         for (my $y = 0; $y <= $gridHeight; $y++) { | ||||
|             my @xPoints = colinearPoints($offset, 0, $gridWidth); | ||||
|             my @yPoints = perpendPoints($offset, $y, $gridWidth); | ||||
|             my @newPoints = map [ $xPoints[$_], $yPoints[$_] ], 0..$#xPoints; | ||||
|              | ||||
|             # trim points to grid edges | ||||
|             #trim(\@newPoints, 0, 0, $gridWidth, $gridHeight); | ||||
|              | ||||
|             if ($y % 2 == 0) { | ||||
|                 push @points, [ @newPoints ]; | ||||
|             } else { | ||||
|                 push @points, [ reverse @newPoints ]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return @points; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -1,91 +0,0 @@ | |||
| package Slic3r::Fill::Base; | ||||
| use Moo; | ||||
| 
 | ||||
| has 'layer_id'            => (is => 'rw'); | ||||
| has 'z'                   => (is => 'rw'); # in unscaled coordinates | ||||
| has 'angle'               => (is => 'rw'); # in radians, ccw, 0 = East | ||||
| has 'spacing'             => (is => 'rw'); # in unscaled coordinates | ||||
| has 'loop_clipping'       => (is => 'rw', default => sub { 0 }); # in scaled coordinates | ||||
| has 'bounding_box'        => (is => 'ro', required => 0);  # Slic3r::Geometry::BoundingBox object | ||||
| 
 | ||||
| sub adjust_solid_spacing { | ||||
|     my $self = shift; | ||||
|     my %params = @_; | ||||
|      | ||||
|     my $number_of_lines = int($params{width} / $params{distance}) + 1; | ||||
|     return $params{distance} if $number_of_lines <= 1; | ||||
|      | ||||
|     my $extra_space = $params{width} % $params{distance}; | ||||
|     return $params{distance} + $extra_space / ($number_of_lines - 1); | ||||
| } | ||||
| 
 | ||||
| sub no_sort { 0 } | ||||
| sub use_bridge_flow { 0 } | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::WithDirection; | ||||
| use Moo::Role; | ||||
| 
 | ||||
| use Slic3r::Geometry qw(PI rad2deg); | ||||
| 
 | ||||
| sub angles () { [0, PI/2] } | ||||
| 
 | ||||
| sub infill_direction { | ||||
|     my $self = shift; | ||||
|     my ($surface) = @_; | ||||
|      | ||||
|     if (!defined $self->angle) { | ||||
|         warn "Using undefined infill angle"; | ||||
|         $self->angle(0); | ||||
|     } | ||||
|      | ||||
|     # set infill angle | ||||
|     my (@rotate); | ||||
|     $rotate[0] = $self->angle; | ||||
|     $rotate[1] = $self->bounding_box | ||||
|         ? $self->bounding_box->center | ||||
|         : $surface->expolygon->bounding_box->center; | ||||
|     my $shift = $rotate[1]->clone; | ||||
|      | ||||
|     if (defined $self->layer_id) { | ||||
|         # alternate fill direction | ||||
|         my $layer_num = $self->layer_id / $surface->thickness_layers; | ||||
|         my $angle = $self->angles->[$layer_num % @{$self->angles}]; | ||||
|         $rotate[0] = $self->angle + $angle if $angle; | ||||
|     } | ||||
|          | ||||
|     # use bridge angle | ||||
|     if ($surface->bridge_angle >= 0) { | ||||
|         Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); | ||||
|         $rotate[0] = $surface->bridge_angle; | ||||
|     } | ||||
|      | ||||
|     $rotate[0] += PI/2; | ||||
|     $shift->rotate(@rotate); | ||||
|     return [\@rotate, $shift]; | ||||
| } | ||||
| 
 | ||||
| # this method accepts any object that implements rotate() and translate() | ||||
| sub rotate_points { | ||||
|     my $self = shift; | ||||
|     my ($expolygon, $rotate_vector) = @_; | ||||
|      | ||||
|     # rotate points | ||||
|     my ($rotate, $shift) = @$rotate_vector; | ||||
|     $rotate = [ -$rotate->[0], $rotate->[1] ]; | ||||
|     $expolygon->rotate(@$rotate); | ||||
|     $expolygon->translate(@$shift); | ||||
| } | ||||
| 
 | ||||
| sub rotate_points_back { | ||||
|     my $self = shift; | ||||
|     my ($paths, $rotate_vector) = @_; | ||||
|      | ||||
|     my ($rotate, $shift) = @$rotate_vector; | ||||
|     $shift = [ map -$_, @$shift ]; | ||||
|      | ||||
|     $_->translate(@$shift) for @$paths; | ||||
|     $_->rotate(@$rotate) for @$paths; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -1,57 +0,0 @@ | |||
| package Slic3r::Fill::Concentric; | ||||
| use Moo; | ||||
| 
 | ||||
| extends 'Slic3r::Fill::Base'; | ||||
| 
 | ||||
| use Slic3r::Geometry qw(scale unscale X); | ||||
| use Slic3r::Geometry::Clipper qw(offset offset2 union_pt_chained); | ||||
| 
 | ||||
| sub no_sort { 1 } | ||||
| 
 | ||||
| sub fill_surface { | ||||
|     my $self = shift; | ||||
|     my ($surface, %params) = @_; | ||||
|      | ||||
|     # no rotation is supported for this infill pattern | ||||
|      | ||||
|     my $expolygon = $surface->expolygon; | ||||
|     my $bounding_box = $expolygon->bounding_box; | ||||
|      | ||||
|     my $min_spacing = scale($self->spacing); | ||||
|     my $distance = $min_spacing / $params{density}; | ||||
|      | ||||
|     if ($params{density} == 1 && !$params{dont_adjust}) { | ||||
|         $distance = $self->adjust_solid_spacing( | ||||
|             width       => $bounding_box->size->[X], | ||||
|             distance    => $distance, | ||||
|         ); | ||||
|         $self->spacing(unscale $distance); | ||||
|     } | ||||
|      | ||||
|     my @loops = my @last = map $_->clone, @$expolygon; | ||||
|     while (@last) { | ||||
|         push @loops, @last = @{offset2(\@last, -($distance + 0.5*$min_spacing), +0.5*$min_spacing)}; | ||||
|     } | ||||
|      | ||||
|     # generate paths from the outermost to the innermost, to avoid  | ||||
|     # adhesion problems of the first central tiny loops | ||||
|     @loops = map Slic3r::Polygon->new(@$_), | ||||
|         reverse @{union_pt_chained(\@loops)}; | ||||
|      | ||||
|     # split paths using a nearest neighbor search | ||||
|     my @paths = (); | ||||
|     my $last_pos = Slic3r::Point->new(0,0); | ||||
|     foreach my $loop (@loops) { | ||||
|         push @paths, $loop->split_at_index($last_pos->nearest_point_index(\@$loop)); | ||||
|         $last_pos = $paths[-1]->last_point; | ||||
|     } | ||||
|      | ||||
|     # clip the paths to prevent the extruder from getting exactly on the first point of the loop | ||||
|     $_->clip_end($self->loop_clipping) for @paths; | ||||
|     @paths = grep $_->is_valid, @paths;  # remove empty paths (too short, thus eaten by clipping) | ||||
|      | ||||
|     # TODO: return ExtrusionLoop objects to get better chained paths | ||||
|     return @paths; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -1,129 +0,0 @@ | |||
| package Slic3r::Fill::Honeycomb; | ||||
| use Moo; | ||||
| 
 | ||||
| extends 'Slic3r::Fill::Base'; | ||||
| with qw(Slic3r::Fill::WithDirection); | ||||
| 
 | ||||
| has 'cache'         => (is => 'rw', default => sub {{}}); | ||||
| 
 | ||||
| use Slic3r::Geometry qw(PI X Y MIN MAX scale scaled_epsilon); | ||||
| use Slic3r::Geometry::Clipper qw(intersection intersection_pl); | ||||
| 
 | ||||
| sub angles () { [0, PI/3, PI/3*2] } | ||||
| 
 | ||||
| sub fill_surface { | ||||
|     my $self = shift; | ||||
|     my ($surface, %params) = @_; | ||||
|      | ||||
|     my $rotate_vector = $self->infill_direction($surface); | ||||
|      | ||||
|     # cache hexagons math | ||||
|     my $cache_id = sprintf "d%s_s%s", $params{density}, $self->spacing; | ||||
|     my $m; | ||||
|     if (!($m = $self->cache->{$cache_id})) { | ||||
|         $m = $self->cache->{$cache_id} = {}; | ||||
|         my $min_spacing = scale($self->spacing); | ||||
|         $m->{distance} = $min_spacing / $params{density}; | ||||
|         $m->{hex_side} = $m->{distance} / (sqrt(3)/2); | ||||
|         $m->{hex_width} = $m->{distance} * 2;  # $m->{hex_width} == $m->{hex_side} * sqrt(3); | ||||
|         my $hex_height = $m->{hex_side} * 2; | ||||
|         $m->{pattern_height} = $hex_height + $m->{hex_side}; | ||||
|         $m->{y_short} = $m->{distance} * sqrt(3)/3; | ||||
|         $m->{x_offset} = $min_spacing / 2; | ||||
|         $m->{y_offset} = $m->{x_offset} * sqrt(3)/3; | ||||
|         $m->{hex_center} = Slic3r::Point->new($m->{hex_width}/2, $m->{hex_side}); | ||||
|     } | ||||
|      | ||||
|     my @polygons = (); | ||||
|     { | ||||
|         # adjust actual bounding box to the nearest multiple of our hex pattern | ||||
|         # and align it so that it matches across layers | ||||
|          | ||||
|         my $bounding_box = $surface->expolygon->bounding_box; | ||||
|         { | ||||
|             # rotate bounding box according to infill direction | ||||
|             my $bb_polygon = $bounding_box->polygon; | ||||
|             $bb_polygon->rotate($rotate_vector->[0][0], $m->{hex_center}); | ||||
|             $bounding_box = $bb_polygon->bounding_box; | ||||
|              | ||||
|             # extend bounding box so that our pattern will be aligned with other layers | ||||
|             # $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one | ||||
|             $bounding_box->merge_point(Slic3r::Point->new( | ||||
|                 $bounding_box->x_min - ($bounding_box->x_min % $m->{hex_width}), | ||||
|                 $bounding_box->y_min - ($bounding_box->y_min % $m->{pattern_height}), | ||||
|             )); | ||||
|         } | ||||
|          | ||||
|         my $x = $bounding_box->x_min; | ||||
|         while ($x <= $bounding_box->x_max) { | ||||
|             my $p = []; | ||||
|              | ||||
|             my @x = ($x + $m->{x_offset}, $x + $m->{distance} - $m->{x_offset}); | ||||
|             for (1..2) { | ||||
|                 @$p = reverse @$p; # turn first half upside down | ||||
|                 my @p = (); | ||||
|                 for (my $y = $bounding_box->y_min; $y <= $bounding_box->y_max; $y += $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side}) { | ||||
|                     push @$p, | ||||
|                         [ $x[1], $y + $m->{y_offset} ], | ||||
|                         [ $x[0], $y + $m->{y_short} - $m->{y_offset} ], | ||||
|                         [ $x[0], $y + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ], | ||||
|                         [ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} - $m->{y_offset} ], | ||||
|                         [ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ]; | ||||
|                 } | ||||
|                 @x = map $_ + $m->{distance}, reverse @x; # draw symmetrical pattern | ||||
|                 $x += $m->{distance}; | ||||
|             } | ||||
|              | ||||
|             push @polygons, Slic3r::Polygon->new(@$p); | ||||
|         } | ||||
|          | ||||
|         $_->rotate(-$rotate_vector->[0][0], $m->{hex_center}) for @polygons; | ||||
|     } | ||||
|      | ||||
|     my @paths; | ||||
|     if ($params{complete} || 1) { | ||||
|         # we were requested to complete each loop; | ||||
|         # in this case we don't try to make more continuous paths | ||||
|         @paths = map $_->split_at_first_point, | ||||
|             @{intersection([ $surface->p ], \@polygons)}; | ||||
|          | ||||
|     } else { | ||||
|         # consider polygons as polylines without re-appending the initial point: | ||||
|         # this cuts the last segment on purpose, so that the jump to the next  | ||||
|         # path is more straight | ||||
|         @paths = @{intersection_pl( | ||||
|             [ map Slic3r::Polyline->new(@$_), @polygons ], | ||||
|             [ @{$surface->expolygon} ], | ||||
|         )}; | ||||
|          | ||||
|         # connect paths | ||||
|         if (@paths) {  # prevent calling leftmost_point() on empty collections | ||||
|             my $collection = Slic3r::Polyline::Collection->new(@paths); | ||||
|             @paths = (); | ||||
|             foreach my $path (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { | ||||
|                 if (@paths) { | ||||
|                     # distance between first point of this path and last point of last path | ||||
|                     my $distance = $paths[-1]->last_point->distance_to($path->first_point); | ||||
|                      | ||||
|                     if ($distance <= $m->{hex_width}) { | ||||
|                         $paths[-1]->append_polyline($path); | ||||
|                         next; | ||||
|                     } | ||||
|                 } | ||||
|                  | ||||
|                 # make a clone before $collection goes out of scope | ||||
|                 push @paths, $path->clone; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         # clip paths again to prevent connection segments from crossing the expolygon boundaries | ||||
|         @paths = @{intersection_pl( | ||||
|             \@paths, | ||||
|             [ map @$_, @{$surface->expolygon->offset_ex(scaled_epsilon)} ], | ||||
|         )}; | ||||
|     } | ||||
|      | ||||
|     return @paths; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -1,118 +0,0 @@ | |||
| package Slic3r::Fill::PlanePath; | ||||
| use Moo; | ||||
| 
 | ||||
| extends 'Slic3r::Fill::Base'; | ||||
| with qw(Slic3r::Fill::WithDirection); | ||||
| 
 | ||||
| use Slic3r::Geometry qw(scale X1 Y1 X2 Y2); | ||||
| use Slic3r::Geometry::Clipper qw(intersection_pl); | ||||
| 
 | ||||
| sub angles () { [0] } | ||||
| sub multiplier () { 1 } | ||||
| 
 | ||||
| sub process_polyline {} | ||||
| 
 | ||||
| sub fill_surface { | ||||
|     my $self = shift; | ||||
|     my ($surface, %params) = @_; | ||||
|      | ||||
|     # rotate polygons | ||||
|     my $expolygon = $surface->expolygon->clone; | ||||
|     my $rotate_vector = $self->infill_direction($surface); | ||||
|     $self->rotate_points($expolygon, $rotate_vector); | ||||
|      | ||||
|     my $distance_between_lines = scale($self->spacing) / $params{density} * $self->multiplier; | ||||
|      | ||||
|     # align infill across layers using the object's bounding box | ||||
|     my $bb_polygon = $self->bounding_box->polygon; | ||||
|     $self->rotate_points($bb_polygon, $rotate_vector); | ||||
|     my $bounding_box = $bb_polygon->bounding_box; | ||||
|      | ||||
|     (ref $self) =~ /::([^:]+)$/; | ||||
|     my $path = "Math::PlanePath::$1"->new; | ||||
|      | ||||
|     my $translate = Slic3r::Point->new(0,0);  # vector | ||||
|     if ($path->x_negative || $path->y_negative) { | ||||
|         # if the curve extends on both positive and negative coordinate space, | ||||
|         # center our expolygon around origin | ||||
|         $translate = $bounding_box->center->negative; | ||||
|     } else { | ||||
|         # if the curve does not extend in negative coordinate space, | ||||
|         # move expolygon entirely in positive coordinate space | ||||
|         $translate = $bounding_box->min_point->negative; | ||||
|     } | ||||
|     $expolygon->translate(@$translate); | ||||
|     $bounding_box->translate(@$translate); | ||||
|      | ||||
|     my ($n_lo, $n_hi) = $path->rect_to_n_range( | ||||
|         map { $_ / $distance_between_lines } | ||||
|             @{$bounding_box->min_point}, | ||||
|             @{$bounding_box->max_point}, | ||||
|     ); | ||||
|      | ||||
|     my $polyline = Slic3r::Polyline->new( | ||||
|         map [ map { $_ * $distance_between_lines } $path->n_to_xy($_) ], ($n_lo..$n_hi) | ||||
|     ); | ||||
|     return {} if @$polyline <= 1; | ||||
|      | ||||
|     $self->process_polyline($polyline, $bounding_box); | ||||
|      | ||||
|     my @paths = @{intersection_pl([$polyline], \@$expolygon)}; | ||||
|      | ||||
|     if (0) { | ||||
|         require "Slic3r/SVG.pm"; | ||||
|         Slic3r::SVG::output("fill.svg", | ||||
|             no_arrows       => 1, | ||||
|             polygons        => \@$expolygon, | ||||
|             green_polygons  => [ $bounding_box->polygon ], | ||||
|             polylines       => [ $polyline ], | ||||
|             red_polylines   => \@paths, | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     # paths must be repositioned and rotated back | ||||
|     $_->translate(@{$translate->negative}) for @paths; | ||||
|     $self->rotate_points_back(\@paths, $rotate_vector); | ||||
|      | ||||
|     return @paths; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::ArchimedeanChords; | ||||
| use Moo; | ||||
| extends 'Slic3r::Fill::PlanePath'; | ||||
| use Math::PlanePath::ArchimedeanChords; | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::Flowsnake; | ||||
| use Moo; | ||||
| extends 'Slic3r::Fill::PlanePath'; | ||||
| use Math::PlanePath::Flowsnake; | ||||
| use Slic3r::Geometry qw(X); | ||||
| 
 | ||||
| # Sorry, this fill is currently broken. | ||||
| 
 | ||||
| sub process_polyline { | ||||
|     my $self = shift; | ||||
|     my ($polyline, $bounding_box) = @_; | ||||
|      | ||||
|     $_->[X] += $bounding_box->center->[X] for @$polyline; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::HilbertCurve; | ||||
| use Moo; | ||||
| extends 'Slic3r::Fill::PlanePath'; | ||||
| use Math::PlanePath::HilbertCurve; | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::OctagramSpiral; | ||||
| use Moo; | ||||
| extends 'Slic3r::Fill::PlanePath'; | ||||
| use Math::PlanePath::OctagramSpiral; | ||||
| 
 | ||||
| sub multiplier () { sqrt(2) } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 1; | ||||
|  | @ -1,168 +0,0 @@ | |||
| package Slic3r::Fill::Rectilinear; | ||||
| use Moo; | ||||
| 
 | ||||
| extends 'Slic3r::Fill::Base'; | ||||
| with qw(Slic3r::Fill::WithDirection); | ||||
| 
 | ||||
| has '_min_spacing'          => (is => 'rw'); | ||||
| has '_line_spacing'         => (is => 'rw'); | ||||
| has '_diagonal_distance'    => (is => 'rw'); | ||||
| has '_line_oscillation'     => (is => 'rw'); | ||||
| 
 | ||||
| use Slic3r::Geometry qw(scale unscale scaled_epsilon); | ||||
| use Slic3r::Geometry::Clipper qw(intersection_pl); | ||||
| 
 | ||||
| sub horizontal_lines { 0 } | ||||
| 
 | ||||
| sub fill_surface { | ||||
|     my $self = shift; | ||||
|     my ($surface, %params) = @_; | ||||
|      | ||||
|     # rotate polygons so that we can work with vertical lines here | ||||
|     my $expolygon = $surface->expolygon->clone; | ||||
|     my $rotate_vector = $self->infill_direction($surface); | ||||
|     $self->rotate_points($expolygon, $rotate_vector); | ||||
|      | ||||
|     $self->_min_spacing(scale $self->spacing); | ||||
|     $self->_line_spacing($self->_min_spacing / $params{density}); | ||||
|     $self->_diagonal_distance($self->_line_spacing * 2); | ||||
|     $self->_line_oscillation($self->_line_spacing - $self->_min_spacing);  # only for Line infill | ||||
|     my $bounding_box = $expolygon->bounding_box; | ||||
|      | ||||
|     # define flow spacing according to requested density | ||||
|     if ($params{density} == 1 && !$params{dont_adjust}) { | ||||
|         $self->_line_spacing($self->adjust_solid_spacing( | ||||
|             width       => $bounding_box->size->x, | ||||
|             distance    => $self->_line_spacing, | ||||
|         )); | ||||
|         $self->spacing(unscale $self->_line_spacing); | ||||
|     } else { | ||||
|         # extend bounding box so that our pattern will be aligned with other layers | ||||
|         $bounding_box->merge_point(Slic3r::Point->new( | ||||
|             $bounding_box->x_min - ($bounding_box->x_min % $self->_line_spacing), | ||||
|             $bounding_box->y_min - ($bounding_box->y_min % $self->_line_spacing), | ||||
|         )); | ||||
|     } | ||||
|      | ||||
|     # generate the basic pattern | ||||
|     my $x_max = $bounding_box->x_max + scaled_epsilon; | ||||
|     my @lines  = (); | ||||
|     for (my $x = $bounding_box->x_min; $x <= $x_max; $x += $self->_line_spacing) { | ||||
|         push @lines, $self->_line($#lines, $x, $bounding_box->y_min, $bounding_box->y_max); | ||||
|     } | ||||
|     if ($self->horizontal_lines) { | ||||
|         my $y_max = $bounding_box->y_max + scaled_epsilon; | ||||
|         for (my $y = $bounding_box->y_min; $y <= $y_max; $y += $self->_line_spacing) { | ||||
|             push @lines, Slic3r::Polyline->new( | ||||
|                 [$bounding_box->x_min, $y], | ||||
|                 [$bounding_box->x_max, $y], | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # clip paths against a slightly larger expolygon, so that the first and last paths | ||||
|     # are kept even if the expolygon has vertical sides | ||||
|     # the minimum offset for preventing edge lines from being clipped is scaled_epsilon; | ||||
|     # however we use a larger offset to support expolygons with slightly skewed sides and  | ||||
|     # not perfectly straight | ||||
|     my @polylines = @{intersection_pl(\@lines, $expolygon->offset(+scale 0.02))}; | ||||
|      | ||||
|     my $extra = $self->_min_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; | ||||
|     foreach my $polyline (@polylines) { | ||||
|         my ($first_point, $last_point) = @$polyline[0,-1]; | ||||
|         if ($first_point->y > $last_point->y) { #> | ||||
|             ($first_point, $last_point) = ($last_point, $first_point); | ||||
|         } | ||||
|         $first_point->set_y($first_point->y - $extra);  #-- | ||||
|         $last_point->set_y($last_point->y + $extra);    #++ | ||||
|     } | ||||
|      | ||||
|     # connect lines | ||||
|     unless ($params{dont_connect} || !@polylines) {  # prevent calling leftmost_point() on empty collections | ||||
|         # offset the expolygon by max(min_spacing/2, extra) | ||||
|         my ($expolygon_off) = @{$expolygon->offset_ex($self->_min_spacing/2)}; | ||||
|         my $collection = Slic3r::Polyline::Collection->new(@polylines); | ||||
|         @polylines = (); | ||||
|          | ||||
|         foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { | ||||
|             if (@polylines) { | ||||
|                 my $first_point = $polyline->first_point; | ||||
|                 my $last_point = $polylines[-1]->last_point; | ||||
|                 my @distance = map abs($first_point->$_ - $last_point->$_), qw(x y); | ||||
|                  | ||||
|                 # TODO: we should also check that both points are on a fill_boundary to avoid  | ||||
|                 # connecting paths on the boundaries of internal regions | ||||
|                 if ($self->_can_connect(@distance) && $expolygon_off->contains_line(Slic3r::Line->new($last_point, $first_point))) { | ||||
|                     $polylines[-1]->append_polyline($polyline); | ||||
|                     next; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             # make a clone before $collection goes out of scope | ||||
|             push @polylines, $polyline->clone; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # paths must be rotated back | ||||
|     $self->rotate_points_back(\@polylines, $rotate_vector); | ||||
|      | ||||
|     return @polylines; | ||||
| } | ||||
| 
 | ||||
| sub _line { | ||||
|     my ($self, $i, $x, $y_min, $y_max) = @_; | ||||
|      | ||||
|     return Slic3r::Polyline->new( | ||||
|         [$x, $y_min], | ||||
|         [$x, $y_max], | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| sub _can_connect { | ||||
|     my ($self, $dist_X, $dist_Y) = @_; | ||||
|      | ||||
|     return $dist_X <= $self->_diagonal_distance | ||||
|         && $dist_Y <= $self->_diagonal_distance; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::Line; | ||||
| use Moo; | ||||
| extends 'Slic3r::Fill::Rectilinear'; | ||||
| 
 | ||||
| use Slic3r::Geometry qw(scaled_epsilon); | ||||
| 
 | ||||
| sub _line { | ||||
|     my ($self, $i, $x, $y_min, $y_max) = @_; | ||||
|      | ||||
|     if ($i % 2) { | ||||
|         return Slic3r::Polyline->new( | ||||
|             [$x - $self->_line_oscillation, $y_min], | ||||
|             [$x + $self->_line_oscillation, $y_max], | ||||
|         ); | ||||
|     } else { | ||||
|         return Slic3r::Polyline->new( | ||||
|             [$x, $y_min], | ||||
|             [$x, $y_max], | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub _can_connect { | ||||
|     my ($self, $dist_X, $dist_Y) = @_; | ||||
|      | ||||
|     my $TOLERANCE = 10 * scaled_epsilon; | ||||
|     return ($dist_X >= ($self->_line_spacing - $self->_line_oscillation) - $TOLERANCE) | ||||
|         && ($dist_X <= ($self->_line_spacing + $self->_line_oscillation) + $TOLERANCE) | ||||
|         && $dist_Y <= $self->_diagonal_distance; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| package Slic3r::Fill::Grid; | ||||
| use Moo; | ||||
| extends 'Slic3r::Fill::Rectilinear'; | ||||
| 
 | ||||
| sub angles () { [0] } | ||||
| sub horizontal_lines { 1 } | ||||
| 
 | ||||
| 1; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv