Honeycomb.pm 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package Slic3r::Fill::Honeycomb;
  2. use Moo;
  3. extends 'Slic3r::Fill::Base';
  4. has 'cache' => (is => 'rw', default => sub {{}});
  5. use Slic3r::Geometry qw(PI X Y MIN MAX scale scaled_epsilon);
  6. use Slic3r::Geometry::Clipper qw(intersection intersection_pl);
  7. sub angles () { [0, PI/3, PI/3*2] }
  8. sub fill_surface {
  9. my $self = shift;
  10. my ($surface, %params) = @_;
  11. my $rotate_vector = $self->infill_direction($surface);
  12. # cache hexagons math
  13. my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow_spacing};
  14. my $m;
  15. if (!($m = $self->cache->{$cache_id})) {
  16. $m = $self->cache->{$cache_id} = {};
  17. my $min_spacing = scale $params{flow_spacing};
  18. $m->{distance} = $min_spacing / $params{density};
  19. $m->{hex_side} = $m->{distance} / (sqrt(3)/2);
  20. $m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3);
  21. my $hex_height = $m->{hex_side} * 2;
  22. $m->{pattern_height} = $hex_height + $m->{hex_side};
  23. $m->{y_short} = $m->{distance} * sqrt(3)/3;
  24. $m->{x_offset} = $min_spacing / 2;
  25. $m->{y_offset} = $m->{x_offset} * sqrt(3)/3;
  26. $m->{hex_center} = Slic3r::Point->new($m->{hex_width}/2, $m->{hex_side});
  27. }
  28. my @polygons = ();
  29. {
  30. # adjust actual bounding box to the nearest multiple of our hex pattern
  31. # and align it so that it matches across layers
  32. my $bounding_box = $surface->expolygon->bounding_box;
  33. {
  34. # rotate bounding box according to infill direction
  35. my $bb_polygon = $bounding_box->polygon;
  36. $bb_polygon->rotate($rotate_vector->[0][0], $m->{hex_center});
  37. $bounding_box = $bb_polygon->bounding_box;
  38. # extend bounding box so that our pattern will be aligned with other layers
  39. # $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
  40. $bounding_box->extents->[X][MIN] -= $bounding_box->x_min % $m->{hex_width};
  41. $bounding_box->extents->[Y][MIN] -= $bounding_box->y_min % $m->{pattern_height};
  42. }
  43. my $x = $bounding_box->x_min;
  44. while ($x <= $bounding_box->x_max) {
  45. my $p = [];
  46. my @x = ($x + $m->{x_offset}, $x + $m->{distance} - $m->{x_offset});
  47. for (1..2) {
  48. @$p = reverse @$p; # turn first half upside down
  49. my @p = ();
  50. 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}) {
  51. push @$p,
  52. [ $x[1], $y + $m->{y_offset} ],
  53. [ $x[0], $y + $m->{y_short} - $m->{y_offset} ],
  54. [ $x[0], $y + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ],
  55. [ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} - $m->{y_offset} ],
  56. [ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ];
  57. }
  58. @x = map $_ + $m->{distance}, reverse @x; # draw symmetrical pattern
  59. $x += $m->{distance};
  60. }
  61. push @polygons, Slic3r::Polygon->new(@$p);
  62. }
  63. $_->rotate(-$rotate_vector->[0][0], $m->{hex_center}) for @polygons;
  64. }
  65. my @paths;
  66. if ($params{complete}) {
  67. # we were requested to complete each loop;
  68. # in this case we don't try to make more continuous paths
  69. @paths = map $_->split_at_first_point,
  70. @{intersection([ $surface->p ], \@polygons)};
  71. } else {
  72. # consider polygons as polylines without re-appending the initial point:
  73. # this cuts the last segment on purpose, so that the jump to the next
  74. # path is more straight
  75. @paths = @{intersection_pl(
  76. [ map Slic3r::Polyline->new(@$_), @polygons ],
  77. [ @{$surface->expolygon} ],
  78. )};
  79. # connect paths
  80. {
  81. my $collection = Slic3r::Polyline::Collection->new(@paths);
  82. @paths = ();
  83. foreach my $path (@{$collection->chained_path_from($collection->leftmost_point, 0)}) {
  84. if (@paths) {
  85. # distance between first point of this path and last point of last path
  86. my $distance = $paths[-1]->last_point->distance_to($path->first_point);
  87. if ($distance <= $m->{hex_width}) {
  88. $paths[-1]->append_polyline($path);
  89. next;
  90. }
  91. }
  92. # make a clone before $collection goes out of scope
  93. push @paths, $path->clone;
  94. }
  95. }
  96. # clip paths again to prevent connection segments from crossing the expolygon boundaries
  97. @paths = @{intersection_pl(
  98. \@paths,
  99. [ @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
  100. )};
  101. }
  102. return { flow_spacing => $params{flow_spacing} }, @paths;
  103. }
  104. 1;