diff --git a/MANIFEST b/MANIFEST index 710940c98..053c24735 100644 --- a/MANIFEST +++ b/MANIFEST @@ -13,6 +13,7 @@ lib/Slic3r/Fill/Base.pm lib/Slic3r/Fill/Concentric.pm lib/Slic3r/Fill/Flowsnake.pm lib/Slic3r/Fill/HilbertCurve.pm +lib/Slic3r/Fill/Honeycomb.pm lib/Slic3r/Fill/Line.pm lib/Slic3r/Fill/OctagramSpiral.pm lib/Slic3r/Fill/PlanePath.pm diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 1a6f10e41..e290da1b2 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -220,8 +220,8 @@ our $Options = { label => 'Fill pattern', cli => 'fill-pattern=s', type => 'select', - values => [qw(rectilinear line concentric hilbertcurve archimedeanchords octagramspiral)], - labels => [qw(rectilinear line concentric), 'hilbertcurve (slow)', 'archimedeanchords (slow)', 'octagramspiral (slow)'], + values => [qw(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)], + labels => [qw(rectilinear line concentric honeycomb), 'hilbertcurve (slow)', 'archimedeanchords (slow)', 'octagramspiral (slow)'], }, 'solid_fill_pattern' => { label => 'Solid fill pattern', diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index f141d6cdc..4a496c674 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -6,6 +6,7 @@ use Slic3r::Fill::Base; use Slic3r::Fill::Concentric; use Slic3r::Fill::Flowsnake; use Slic3r::Fill::HilbertCurve; +use Slic3r::Fill::Honeycomb; use Slic3r::Fill::Line; use Slic3r::Fill::OctagramSpiral; use Slic3r::Fill::PlanePath; @@ -28,6 +29,7 @@ our %FillTypes = ( hilbertcurve => 'Slic3r::Fill::HilbertCurve', line => 'Slic3r::Fill::Line', concentric => 'Slic3r::Fill::Concentric', + honeycomb => 'Slic3r::Fill::Honeycomb', ); sub BUILD { diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index 63e3cedd9..c8054e6b7 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -3,11 +3,14 @@ use Moo; use XXX; +has 'print' => (is => 'rw'); has 'layer' => (is => 'rw'); has 'max_print_dimension' => (is => 'rw'); use constant PI => 4 * atan2(1, 1); +sub angles () { [0, PI/2] } + sub infill_direction { my $self = shift; my ($surface) = @_; @@ -20,9 +23,9 @@ sub infill_direction { if ($self->layer) { # alternate fill direction - if (($self->layer->id / $surface->depth_layers) % 2) { - $rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2; - } + my $layer_num = $self->layer->id / $surface->depth_layers; + my $angle = $self->angles->[$layer_num % @{$self->angles}]; + $rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + $angle if $angle; } # use bridge angle diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm new file mode 100644 index 000000000..4f2cda463 --- /dev/null +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -0,0 +1,95 @@ +package Slic3r::Fill::Honeycomb; +use Moo; + +extends 'Slic3r::Fill::Base'; + +has 'cache' => (is => 'rw', default => sub {{}}); + +use Slic3r::Geometry qw(PI X1 Y1 X2 Y2 X Y scale); +use Slic3r::Geometry::Clipper qw(intersection_ex); +use XXX; + +sub angles () { [0, PI/3, PI/3*2] } + +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); + + # infill math + my $min_spacing = scale $params{flow_spacing}; + my $distance = $min_spacing / $params{density}; + my $overlap_distance = scale $Slic3r::flow_width * 0.4; + + my $cache_id = sprintf "d%s_s%s_a%s", + $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; + if (!$self->cache->{$cache_id}) { + + # hexagons math + my $hex_side = $distance / (sqrt(3)/2); + my $hex_width = $distance * 2; # $hex_width == $hex_side * sqrt(3); + my $hex_height = $hex_side * 2; + my $pattern_height = $hex_height + $hex_side; + my $y_short = $distance * sqrt(3)/3; + my $x_offset = $min_spacing / 2; + my $y_offset = $x_offset * sqrt(3)/3; + my $hex_center = Slic3r::Point->new($hex_width/2, $hex_side); + + # adjust actual bounding box to the nearest multiple of our hex pattern + # and align it so that it matches across layers + my $bounding_box = [ 0, 0, $self->print->x_length, $self->print->y_length ]; + { + my $bb_polygon = Slic3r::Polygon->new([ + [ $bounding_box->[X1], $bounding_box->[Y1] ], + [ $bounding_box->[X2], $bounding_box->[Y1] ], + [ $bounding_box->[X2], $bounding_box->[Y2] ], + [ $bounding_box->[X1], $bounding_box->[Y2] ], + ]); + $bb_polygon->rotate($rotate_vector->[0][0], $hex_center); + $bounding_box = [ Slic3r::Geometry::bounding_box($bb_polygon) ]; + # $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one + $bounding_box->[X1] -= $bounding_box->[X1] % $hex_width; + $bounding_box->[Y1] -= $bounding_box->[Y1] % $pattern_height; + } + + my @polygons = (); + my $x = $bounding_box->[X1]; + while ($x <= $bounding_box->[X2]) { + my $p = []; + + my @x = ($x + $x_offset, $x + $distance - $x_offset); + for (1..2) { + @$p = reverse @$p; # turn first half upside down + my @p = (); + for (my $y = $bounding_box->[Y1]; $y <= $bounding_box->[Y2]; $y += $y_short + $hex_side + $y_short + $hex_side) { + push @$p, + [ $x[1], $y + $y_offset ], + [ $x[0], $y + $y_short - $y_offset ], + [ $x[0], $y + $y_short + $hex_side + $y_offset ], + [ $x[1], $y + $y_short + $hex_side + $y_short - $y_offset ], + [ $x[1], $y + $y_short + $hex_side + $y_short + $hex_side + $y_offset ]; + } + @x = map $_ + $distance, reverse @x; # draw symmetrical pattern + $x += $distance; + } + + push @polygons, Slic3r::Polygon->new($p); + } + + $_->rotate(-$rotate_vector->[0][0], $hex_center) for @polygons; + $self->cache->{$cache_id} = [@polygons]; + } + + my $loops = intersection_ex( + $self->cache->{$cache_id}, + [ map @$_, $expolygon->offset_ex($overlap_distance) ], + ); + my @paths = map [ @$_, $_->[0] ], map @$_, @$loops; + + return {}, @paths; +} + +1; diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index 5d471e3b3..ffa7fa1a8 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -54,6 +54,18 @@ sub distance_to { return Slic3r::Geometry::distance_between_points($self, $point); } +sub rotate { + my $self = shift; + my ($angle, $center) = @_; + @$self = @{ +(Slic3r::Geometry::rotate_points($angle, $center, $self))[0] }; +} + +sub translate { + my $self = shift; + my ($x, $y) = @_; + @$self = @{ +(Slic3r::Geometry::move_points([$x, $y], $self))[0] }; +} + sub x { $_[0]->[0] } sub y { $_[0]->[1] } diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 110ba2b5c..c5296fb3e 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -6,8 +6,7 @@ use warnings; use parent 'Slic3r::Polyline'; use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges - scale polygon_remove_acute_vertices - polygon_segment_having_point point_in_polygon move_points rotate_points); + scale polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon); use Slic3r::Geometry::Clipper qw(JT_MITER); # the constructor accepts an array(ref) of points @@ -83,18 +82,6 @@ sub encloses_point { return point_in_polygon($point, $self); } -sub translate { - my $self = shift; - my ($x, $y) = @_; - @$self = move_points([$x, $y], @$self); -} - -sub rotate { - my $self = shift; - my ($angle, $center) = @_; - @$self = rotate_points($angle, $center, @$self); -} - sub area { my $self = shift; return Slic3r::Geometry::Clipper::area($self); diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index cb7339572..5b8a09b8c 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -114,10 +114,16 @@ sub bounding_box { return Slic3r::Geometry::bounding_box($self); } +sub rotate { + my $self = shift; + my ($angle, $center) = @_; + @$self = Slic3r::Geometry::rotate_points($angle, $center, @$self); +} + sub translate { my $self = shift; my ($x, $y) = @_; - @$self = move_points([$x, $y], @$self); + @$self = Slic3r::Geometry::move_points([$x, $y], @$self); } 1;